package co.bugu.code.analysis;

import cn.idev.excel.EasyExcel;
import cn.idev.excel.annotation.ExcelProperty;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.apache.poi.ss.usermodel.Workbook;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.*;

public class GitLabCodeChangeCollector {

    private static final String GITLAB_API_URL = "https://gitlab.lbxdrugs.com"; // GitLab API地址

//    private static final String GITLAB_API_URL = "https://gitlab.example.com"; // 替换为你的 GitLab 地址
    private static final String PRIVATE_TOKEN = "glpat-RpVcssru2KxRopZsmR_s";         // 替换为你的 GitLab Token

    public static class CodeChangeStats {
        @ExcelProperty("项目id")
        private final int projectId;
        @ExcelProperty("项目名称")
        private final String projectName;
        @ExcelProperty("添加行数")
        private final int additions;
        @ExcelProperty("删除行数")
        private final int deletions;

        public CodeChangeStats(int projectId, String projectName, int additions, int deletions) {
            this.projectId = projectId;
            this.projectName = projectName;
            this.additions = additions;
            this.deletions = deletions;
        }

        public int getProjectId() {
            return projectId;
        }

        public String getProjectName() {
            return projectName;
        }

        public int getAdditions() {
            return additions;
        }

        public int getDeletions() {
            return deletions;
        }
    }

    // ====================================================================================
    // === 主流程入口
    // ====================================================================================

    public static void main(String[] args) {
        try {
            String since = DateTimeFormatter.ISO_INSTANT.format(Instant.now().minusSeconds(60 * 60 * 24 * 7)); // 一周前
            String until = DateTimeFormatter.ISO_INSTANT.format(Instant.now());

            List<CodeChangeStats> changes = getProjectChangesInPeriodConcurrently(since, until);

            String outputFilePath = "D:/gitlab_code_change_stats.xlsx";
            exportToExcelWithFastExcel(changes, outputFilePath);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // ====================================================================================
    // === 数据获取部分
    // ====================================================================================

    public static List<CodeChangeStats> getProjectChangesInPeriodConcurrently(String since, String until) throws Exception {
        List<CodeChangeStats> result = new ArrayList<>();

        List<JSONObject> projects = getAllProjectsPaginated();

        ExecutorService executor = Executors.newFixedThreadPool(10);
        List<Future<CodeChangeStats>> futures = new ArrayList<>();

        for (JSONObject project : projects) {
            Future<CodeChangeStats> future = executor.submit(() -> processProject(project, since, until));
            futures.add(future);
        }

        for (Future<CodeChangeStats> future : futures) {
            try {
                CodeChangeStats stats = future.get();
                if (stats != null) {
                    result.add(stats);
                }
            } catch (Exception e) {
                System.err.println("获取项目统计结果失败: " + e.getMessage());
            }
        }

        executor.shutdown();
        return result;
    }

    private static CodeChangeStats processProject(JSONObject project, String since, String until) {
        try {
            int projectId = project.getIntValue("id");
            String projectName = project.getString("name");

            int totalAdditions = 0;
            int totalDeletions = 0;

            List<JSONObject> branches = getAllBranchesPaginated(projectId);

            for (JSONObject branch : branches) {
                String branchName = branch.getString("name");

                JSONObject compareResult = compareBranchByRef(projectId, branchName, since, until);
                if (compareResult == null || !compareResult.containsKey("diffs")) {
                    continue;
                }

                JSONArray diffs = compareResult.getJSONArray("diffs");

                int additions = 0;
                int deletions = 0;

                for (Object obj : diffs) {
                    JSONObject diff = (JSONObject) obj;
                    additions += diff.getIntValue("added_lines");
                    deletions += diff.getIntValue("removed_lines");
                }

                totalAdditions += additions;
                totalDeletions += deletions;
            }

            return new CodeChangeStats(projectId, projectName, totalAdditions, totalDeletions);
        } catch (Exception e) {
            System.err.println("处理项目失败: " + project.getString("name") + ", 错误: " + e.getMessage());
            return null;
        }
    }

    // ====================================================================================
    // === 分页获取项目和分支
    // ====================================================================================

    private static List<JSONObject> getAllProjectsPaginated() throws IOException {
        List<JSONObject> allProjects = new ArrayList<>();
        int page = 1;
        int totalPages = 1;

        while (page <= totalPages) {
            String urlString = GITLAB_API_URL + "/api/v4/projects?per_page=100&page=" + page;
            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("PRIVATE-TOKEN", PRIVATE_TOKEN);

            if (conn.getResponseCode() != 200) {
                throw new RuntimeException("无法获取项目列表: HTTP 错误码 " + conn.getResponseCode());
            }

            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            StringBuilder response = new StringBuilder();
            String line;

            while ((line = reader.readLine()) != null) {
                response.append(line);
            }

            JSONArray projects = JSON.parseArray(response.toString());
            for (Object obj : projects) {
                allProjects.add((JSONObject) obj);
            }

            totalPages = Integer.parseInt(conn.getHeaderField("X-Total-Pages"));
            page++;
        }

        return allProjects;
    }

    private static List<JSONObject> getAllBranchesPaginated(int projectId) throws IOException {
        List<JSONObject> allBranches = new ArrayList<>();
        int page = 1;
        int totalPages = 1;

        while (page <= totalPages) {
            String urlString = String.format(
                    "%s/api/v4/projects/%d/repository/branches?per_page=100&page=%d",
                    GITLAB_API_URL, projectId, page
            );

            URL url = new URL(urlString);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setRequestProperty("PRIVATE-TOKEN", PRIVATE_TOKEN);

            if (conn.getResponseCode() != 200) {
                throw new RuntimeException("无法获取分支列表: HTTP 错误码 " + conn.getResponseCode());
            }

            BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            StringBuilder response = new StringBuilder();
            String line;

            while ((line = reader.readLine()) != null) {
                response.append(line);
            }

            JSONArray branches = JSON.parseArray(response.toString());
            for (Object obj : branches) {
                allBranches.add((JSONObject) obj);
            }

            totalPages = Integer.parseInt(conn.getHeaderField("X-Total-Pages"));
            page++;
        }

        return allBranches;
    }

    // ====================================================================================
    // === 调用 GitLab API 获取变更
    // ====================================================================================

    private static JSONObject compareBranchByRef(int projectId, String ref, String since, String until) throws IOException {
//        String encodedSince = URLEncoder.encode(since, StandardCharsets.UTF_8);
//        String encodedUntil = URLEncoder.encode(until, StandardCharsets.UTF_8);

        String encodedSince = null;
        String encodedUntil = null;
        String urlString = String.format(
                "%s/api/v4/projects/%d/repository/compare?from=%s&to=%s&straight=true",
                GITLAB_API_URL, projectId,
                ref + "~{\"" + encodedSince + "\"}",
                ref + "^{\"" + encodedUntil + "\"}"
        );

        URL url = new URL(urlString);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("PRIVATE-TOKEN", PRIVATE_TOKEN);

        if (conn.getResponseCode() != 200) {
            System.err.println("无法比较项目 " + projectId + " 分支 " + ref + ": HTTP 错误码 " + conn.getResponseCode());
            return null;
        }

        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        StringBuilder response = new StringBuilder();
        String line;

        while ((line = reader.readLine()) != null) {
            response.append(line);
        }

        return JSON.parseObject(response.toString());
    }


    // ====================================================================================
    // === Excel 导出部分（FastExcel）
    // ====================================================================================

    public static void exportToExcelWithFastExcel(List<CodeChangeStats> statsList, String filePath) throws Exception {
        EasyExcel.write(new File(filePath)).sheet("GitLab 代码变更统计")

                .head(CodeChangeStats.class)
                .doWrite(statsList);

    }

    /**
     * 获取指定项目在某个时间段内的代码变更统计
     *
     * @param projectId 项目 ID
     * @param since     开始时间 (ISO8601 格式)
     * @param until     结束时间 (ISO8601 格式)
     * @return CodeChangeStats 包含添加和删除的行数
     */
    public static CodeChangeStats getCodeChangeStatsForProject(int projectId, String since, String until) {
        try {
            // 获取项目名称
            String projectName = getProjectNameById(projectId);

            int totalAdditions = 0;
            int totalDeletions = 0;

            // 获取所有分支
            List<JSONObject> branches = getAllBranchesPaginated(projectId);

            for (JSONObject branch : branches) {
                String branchName = branch.getString("name");

                // 比较指定时间段内的 diff
                JSONObject compareResult = compareBranchByRef(projectId, branchName, since, until);
                if (compareResult == null || !compareResult.containsKey("diffs")) {
                    continue;
                }

                JSONArray diffs = compareResult.getJSONArray("diffs");
                for (Object obj : diffs) {
                    JSONObject diff = (JSONObject) obj;
                    totalAdditions += diff.getIntValue("added_lines");
                    totalDeletions += diff.getIntValue("removed_lines");
                }
            }

            return new CodeChangeStats(projectId, projectName, totalAdditions, totalDeletions);

        } catch (Exception e) {
            System.err.println("获取项目 " + projectId + " 的统计信息失败: " + e.getMessage());
            return new CodeChangeStats(projectId, "未知项目", 0, 0);
        }
    }

    /**
     * 通过项目ID获取项目名称
     */
    private static String getProjectNameById(int projectId) throws IOException {
        String urlString = GITLAB_API_URL + "/api/v4/projects/" + projectId;
        URL url = new URL(urlString);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");
        conn.setRequestProperty("PRIVATE-TOKEN", PRIVATE_TOKEN);

        if (conn.getResponseCode() != 200) {
            throw new RuntimeException("无法获取项目名称: HTTP 错误码 " + conn.getResponseCode());
        }

        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        StringBuilder response = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            response.append(line);
        }

        JSONObject projectInfo = JSON.parseObject(response.toString());
        return projectInfo.getString("name");
    }


    /**
     * 获取 GitLab 项目在指定时间段内的所有提交及其变更内容
     *
     * @param gitLabUrl   GitLab 地址，例如：https://gitlab.example.com
     * @param projectId   项目 ID 或 URL 编码路径
     * @param privateToken 访问令牌
     * @param since       开始时间（ISO8601 格式）
     * @param until       结束时间（ISO8601 格式）
     */
    public static void fetchGitLabCommitsInTimeRange(
            String projectId,
            String since, String until) {

        try {
            // 构建获取提交记录的 URL
            String commitsUrl = String.format("%s/api/v4/projects/%s/repository/commits?since=%s&until=%s&private_token=%s",
                    GITLAB_API_URL, projectId, since, until, PRIVATE_TOKEN);

            // 获取提交列表
            JSONArray commits = fetchJSONArray(commitsUrl);
            System.out.println("Total Commits: " + commits.size());

            for (int i = 0; i < commits.size(); i++) {
                JSONObject commit = commits.getJSONObject(i);
                String commitId = commit.getString("id");
                String message = commit.getString("message");
                String author = commit.getString("author_name");

                System.out.printf("Commit ID: %s%nMessage: %s%nAuthor: %s%n", commitId, message, author);

                // 获取该提交的 diff 变更
                String diffUrl = String.format("%s/api/v4/projects/%s/repository/commits/%s/diff?private_token=%s",
                        GITLAB_API_URL, projectId, commitId, PRIVATE_TOKEN);
                JSONArray diff = fetchJSONArray(diffUrl);

                for (int j = 0; j < diff.size(); j++) {
                    JSONObject change = diff.getJSONObject(j);
                    String filePath = change.getString("new_path");
                    String diffContent = change.getString("diff");

                    System.out.printf("File Changed: %s%nDiff Content:%n%s%n", filePath, diffContent);
                }
            }

        } catch (Exception e) {
            System.err.println("Error fetching commits: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 发送 GET 请求并返回 JSONArray
     */
    private static JSONArray fetchJSONArray(String urlString) throws IOException {
        URL url = new URL(urlString);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");

        if (conn.getResponseCode() != 200) {
            throw new IOException("HTTP error code: " + conn.getResponseCode());
        }

        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
        StringBuilder response = new StringBuilder();
        String line;

        while ((line = reader.readLine()) != null) {
            response.append(line);
        }

        return new JSONArray(response.toString());
    }
}
